/*
 * Fuel UX Wizard
 * https://github.com/ExactTarget/fuelux
 *
 * Copyright (c) 2014 ExactTarget
 * Licensed under the BSD New license.
 */

// -- BEGIN UMD WRAPPER PREFACE --

// For more information on UMD visit:
// https://github.com/umdjs/umd/blob/master/jqueryPlugin.js

(function(factory) {
    if (typeof define === "function" && define.amd) {
        // if AMD loader is available, register as an anonymous module.
        define(["jquery"], factory);
    } else if (typeof exports === "object") {
        // Node/CommonJS
        module.exports = factory(require("jquery"));
    } else {
        // OR use browser globals if AMD is not present
        factory(jQuery);
    }
}(function($) {
    // -- END UMD WRAPPER PREFACE --

    // -- BEGIN MODULE CODE HERE --

    var old = $.fn.wizard;

    // WIZARD CONSTRUCTOR AND PROTOTYPE

    var Wizard = function(element, options) {
        var kids;

        this.$element = $(element);
        this.options = $.extend({}, $.fn.wizard.defaults, options);
        this.options.disablePreviousStep = (this.$element.attr("data-restrict") === "previous")
            ? true
            : this.options.disablePreviousStep;
        this.currentStep = this.options.selectedItem.step;
        this.numSteps = this.$element.find(".steps li").length;
        this.$prevBtn = this.$element.find("button.btn-prev");
        this.$nextBtn = this.$element.find("button.btn-next");

        // maintains backwards compatibility with < 3.8, will be removed in the future
        if (this.$element.children(".steps-container").length === 0) {
            this.$element.addClass("no-steps-container");
            if (window && window.console && window.console.warn) {
                window.console.warn(
                    'please update your wizard markup to include ".steps-container" as seen in http://getfuelux.com/javascript.html#wizard-usage-markup');
            }
        }

        kids = this.$nextBtn.children().detach();
        this.nextText = $.trim(this.$nextBtn.text());
        this.$nextBtn.append(kids);

        // handle events
        this.$prevBtn.on("click.fu.wizard", $.proxy(this.previous, this));
        this.$nextBtn.on("click.fu.wizard", $.proxy(this.next, this));
        this.$element.on("click.fu.wizard", "li.complete", $.proxy(this.stepclicked, this));

        this.selectedItem(this.options.selectedItem);

        if (this.options.disablePreviousStep) {
            this.$prevBtn.attr("disabled", true);
            this.$element.find(".steps").addClass("previous-disabled");
        }
    };

    Wizard.prototype = {
        constructor: Wizard,

        destroy: function() {
            this.$element.remove();
            // any external bindings [none]
            // empty elements to return to original markup [none]
            // returns string of markup
            return this.$element[0].outerHTML;
        },

        //index is 1 based
        //second parameter can be array of objects [{ ... }, { ... }] or you can pass n additional objects as args
        //object structure is as follows (all params are optional): { badge: '', label: '', pane: '' }
        addSteps: function(index) {
            var items = [].slice.call(arguments).slice(1);
            var $steps = this.$element.find(".steps");
            var $stepContent = this.$element.find(".step-content");
            var i, l, $pane, $startPane, $startStep, $step;

            index = (index === -1 || (index > (this.numSteps + 1))) ? this.numSteps + 1 : index;
            if (items[0] instanceof Array) {
                items = items[0];
            }

            $startStep = $steps.find("li:nth-child(" + index + ")");
            $startPane = $stepContent.find(".step-pane:nth-child(" + index + ")");
            if ($startStep.length < 1) {
                $startStep = null;
            }

            for (i = 0, l = items.length; i < l; i++) {
                $step = $('<li data-step="' + index + '"><span class="badge badge-info"></span></li>');
                $step.append(items[i].label || "").append('<span class="chevron"></span>');
                $step.find(".badge").append(items[i].badge || index);

                $pane = $('<div class="step-pane" data-step="' + index + '"></div>');
                $pane.append(items[i].pane || "");

                if (!$startStep) {
                    $steps.append($step);
                    $stepContent.append($pane);
                } else {
                    $startStep.before($step);
                    $startPane.before($pane);
                }

                index++;
            }

            this.syncSteps();
            this.numSteps = $steps.find("li").length;
            this.setState();
        },

        //index is 1 based, howMany is number to remove
        removeSteps: function(index, howMany) {
            var action = "nextAll";
            var i = 0;
            var $steps = this.$element.find(".steps");
            var $stepContent = this.$element.find(".step-content");
            var $start;

            howMany = (howMany !== undefined) ? howMany : 1;

            if (index > $steps.find("li").length) {
                $start = $steps.find("li:last");
            } else {
                $start = $steps.find("li:nth-child(" + index + ")").prev();
                if ($start.length < 1) {
                    action = "children";
                    $start = $steps;
                }

            }

            $start[action]().each(function() {
                var item = $(this);
                var step = item.attr("data-step");
                if (i < howMany) {
                    item.remove();
                    $stepContent.find('.step-pane[data-step="' + step + '"]:first').remove();
                } else {
                    return false;
                }

                i++;
            });

            this.syncSteps();
            this.numSteps = $steps.find("li").length;
            this.setState();
        },

        setState: function() {
            var canMovePrev = (this.currentStep > 1); //remember, steps index is 1 based...
            var isFirstStep = (this.currentStep === 1);
            var isLastStep = (this.currentStep === this.numSteps);

            // disable buttons based on current step
            if (!this.options.disablePreviousStep) {
                this.$prevBtn.attr("disabled", (isFirstStep === true || canMovePrev === false));
            }

            // change button text of last step, if specified
            var last = this.$nextBtn.attr("data-last");
            if (last) {
                this.lastText = last;
                // replace text
                var text = this.nextText;
                if (isLastStep === true) {
                    text = this.lastText;
                    // add status class to wizard
                    this.$element.addClass("complete");
                } else {
                    this.$element.removeClass("complete");
                }

                var kids = this.$nextBtn.children().detach();
                this.$nextBtn.text(text).append(kids);
            }

            // reset classes for all steps
            var $steps = this.$element.find(".steps li");
            $steps.removeClass("active").removeClass("complete");
            $steps.find("span.badge").removeClass("badge-info").removeClass("badge-success");

            // set class for all previous steps
            var prevSelector = ".steps li:lt(" + (this.currentStep - 1) + ")";
            var $prevSteps = this.$element.find(prevSelector);
            $prevSteps.addClass("complete");
            $prevSteps.find("span.badge").addClass("badge-success");

            // set class for current step
            var currentSelector = ".steps li:eq(" + (this.currentStep - 1) + ")";
            var $currentStep = this.$element.find(currentSelector);
            $currentStep.addClass("active");
            $currentStep.find("span.badge").addClass("badge-info");

            // set display of target element
            var $stepContent = this.$element.find(".step-content");
            var target = $currentStep.attr("data-step");
            $stepContent.find(".step-pane").removeClass("active");
            $stepContent.find('.step-pane[data-step="' + target + '"]:first').addClass("active");

            // reset the wizard position to the left
            this.$element.find(".steps").first().attr("style", "margin-left: 0");

            // check if the steps are wider than the container div
            var totalWidth = 0;
            this.$element.find(".steps > li").each(function() {
                totalWidth += $(this).outerWidth();
            });
            var containerWidth = 0;
            if (this.$element.find(".actions").length) {
                containerWidth = this.$element.width() - this.$element.find(".actions").first().outerWidth();
            } else {
                containerWidth = this.$element.width();
            }

            if (totalWidth > containerWidth) {
                // set the position so that the last step is on the right
                var newMargin = totalWidth - containerWidth;
                this.$element.find(".steps").first().attr("style", "margin-left: -" + newMargin + "px");

                // set the position so that the active step is in a good
                // position if it has been moved out of view
                if (this.$element.find("li.active").first().position().left < 200) {
                    newMargin += this.$element.find("li.active").first().position().left - 200;
                    if (newMargin < 1) {
                        this.$element.find(".steps").first().attr("style", "margin-left: 0");
                    } else {
                        this.$element.find(".steps").first().attr("style", "margin-left: -" + newMargin + "px");
                    }

                }

            }

            // only fire changed event after initializing
            if (typeof (this.initialized) !== "undefined") {
                var e = $.Event("changed.fu.wizard");
                this.$element.trigger(e,
                    {
                        step: this.currentStep
                    });
            }

            this.initialized = true;
        },

        stepclicked: function(e) {
            var li = $(e.currentTarget);
            var index = this.$element.find(".steps li").index(li);

            if (index < this.currentStep && this.options.disablePreviousStep) { //enforce restrictions
                return;
            } else {
                var evt = $.Event("stepclicked.fu.wizard");
                this.$element.trigger(evt,
                    {
                        step: index + 1
                    });
                if (evt.isDefaultPrevented()) {
                    return;
                }

                this.currentStep = (index + 1);
                this.setState();
            }
        },

        syncSteps: function() {
            var i = 1;
            var $steps = this.$element.find(".steps");
            var $stepContent = this.$element.find(".step-content");

            $steps.children().each(function() {
                var item = $(this);
                var badge = item.find(".badge");
                var step = item.attr("data-step");

                if (!isNaN(parseInt(badge.html(), 10))) {
                    badge.html(i);
                }

                item.attr("data-step", i);
                $stepContent.find('.step-pane[data-step="' + step + '"]:last').attr("data-step", i);
                i++;
            });
        },

        previous: function() {
            if (this.options.disablePreviousStep || this.currentStep === 1) {
                return;
            }

            var e = $.Event("actionclicked.fu.wizard");
            this.$element.trigger(e,
                {
                    step: this.currentStep,
                    direction: "previous"
                });
            if (e.isDefaultPrevented()) {
                return;
            } // don't increment ...what? Why?

            this.currentStep -= 1;
            this.setState();

            // only set focus if focus is still on the $nextBtn (avoid stomping on a focus set programmatically in actionclicked callback)
            if (this.$prevBtn.is(":focus")) {
                var firstFormField = this.$element.find(".active").find("input, select, textarea")[0];

                if (typeof firstFormField !== "undefined") {
                    // allow user to start typing immediately instead of having to click on the form field.
                    $(firstFormField).focus();
                } else if (this.$element.find(".active input:first").length === 0 && this.$prevBtn.is(":disabled")) {
                    //only set focus on a button as the last resort if no form fields exist and the just clicked button is now disabled
                    this.$nextBtn.focus();
                }

            }
        },

        next: function() {
            var e = $.Event("actionclicked.fu.wizard");
            this.$element.trigger(e,
                {
                    step: this.currentStep,
                    direction: "next"
                });
            if (e.isDefaultPrevented()) {
                return;
            } // respect preventDefault in case dev has attached validation to step and wants to stop propagation based on it.

            if (this.currentStep < this.numSteps) {
                this.currentStep += 1;
                this.setState();
            } else { //is last step
                this.$element.trigger("finished.fu.wizard");
            }

            // only set focus if focus is still on the $nextBtn (avoid stomping on a focus set programmatically in actionclicked callback)
            if (this.$nextBtn.is(":focus")) {
                var firstFormField = this.$element.find(".active").find("input, select, textarea")[0];

                if (typeof firstFormField !== "undefined") {
                    // allow user to start typing immediately instead of having to click on the form field.
                    $(firstFormField).focus();
                } else if (this.$element.find(".active input:first").length === 0 && this.$nextBtn.is(":disabled")) {
                    //only set focus on a button as the last resort if no form fields exist and the just clicked button is now disabled
                    this.$prevBtn.focus();
                }

            }
        },

        selectedItem: function(selectedItem) {
            var retVal, step;

            if (selectedItem) {
                step = selectedItem.step || -1;
                //allow selection of step by data-name
                step = Number(this.$element.find('.steps li[data-name="' + step + '"]').first().attr("data-step")) ||
                    Number(step);

                if (1 <= step && step <= this.numSteps) {
                    this.currentStep = step;
                    this.setState();
                } else {
                    step = this.$element.find(".steps li.active:first").attr("data-step");
                    if (!isNaN(step)) {
                        this.currentStep = parseInt(step, 10);
                        this.setState();
                    }

                }

                retVal = this;
            } else {
                retVal = {
                    step: this.currentStep
                };
                if (this.$element.find(".steps li.active:first[data-name]").length) {
                    retVal.stepname = this.$element.find(".steps li.active:first").attr("data-name");
                }

            }

            return retVal;
        }
    };


    // WIZARD PLUGIN DEFINITION

    $.fn.wizard = function(option) {
        var args = Array.prototype.slice.call(arguments, 1);
        var methodReturn;

        var $set = this.each(function() {
            var $this = $(this);
            var data = $this.data("fu.wizard");
            var options = typeof option === "object" && option;

            if (!data) {
                $this.data("fu.wizard", (data = new Wizard(this, options)));
            }

            if (typeof option === "string") {
                methodReturn = data[option].apply(data, args);
            }
        });

        return (methodReturn === undefined) ? $set : methodReturn;
    };

    $.fn.wizard.defaults = {
        disablePreviousStep: false,
        selectedItem: {
            step: -1
        } //-1 means it will attempt to look for "active" class in order to set the step
    };

    $.fn.wizard.Constructor = Wizard;

    $.fn.wizard.noConflict = function() {
        $.fn.wizard = old;
        return this;
    };


    // DATA-API

    $(document).on("mouseover.fu.wizard.data-api",
        "[data-initialize=wizard]",
        function(e) {
            var $control = $(e.target).closest(".wizard");
            if (!$control.data("fu.wizard")) {
                $control.wizard($control.data());
            }
        });

    // Must be domReady for AMD compatibility
    $(function() {
        $("[data-initialize=wizard]").each(function() {
            var $this = $(this);
            if ($this.data("fu.wizard")) return;
            $this.wizard($this.data());
        });
    });

    // -- BEGIN UMD WRAPPER AFTERWORD --
}));
// -- END UMD WRAPPER AFTERWORD --
